
// Copyright (c) 2009 Mohannad Alharthi (mohannad.harthi@gmail.com)
// All rights reserved.
// This source code is licensed under the BSD license, which can be found in
// the LICENSE.txt file.

#include "parser.h"
#include "misc.h"

Parser::Parser(Lexer* lexer, std::vector<Message>* errors_list)
      : errors_list_(errors_list),
        lexer_(lexer),
        root_symbol_table_(new SymbolTable(NULL)),
        temp_counter_(0),
        default_type_(INT_TYPE, false, false) {

  current_scope_table_ = root_symbol_table_;
  reserveKeywords();

  initial_block_ = new BlockStatement;
  current_block_ = initial_block_;

  current_token_ = lexer_->getNextToken(*current_scope_table_);
}

Parser::~Parser() {
  // TODO: Dellocating symbol tables.
}

void Parser::reserveKeywords() {
  RESERVE("int", INT);
  RESERVE("char", CHAR);
  RESERVE("void", VOID);
  RESERVE("if", IF);
  RESERVE("else", ELSE);
  RESERVE("for", FOR);
  RESERVE("do", DO);
  RESERVE("while", WHILE);
  RESERVE("switch", SWITCH);
  RESERVE("case", CASE);
  RESERVE("default", DEFAULT);
  RESERVE("return", RETURN);
  RESERVE("break", BREAK);
  RESERVE("continue", CONTINUE);
  RESERVE("printInt", PRINT_INT);
  RESERVE("printStr", PRINT_STR);
  RESERVE("printChar", PRINT_CHAR);
  RESERVE("readStr", READ_STR);
  RESERVE("readInt", READ_INT);
}

void Parser::error(const std::string& message_str) {
  Location loc(lexer_->source_file(), lexer_->current_location().line(), 0);
  Message message(message_str, loc);
  errors_list_->push_back(message);
}

bool Parser::matchIf(TokenCode code) {
  if (code == current_token_.code()) {
    current_token_ = lexer_->getNextToken(*current_scope_table_);
    return true;
  }
  return false;
}

void Parser::match(TokenCode code) {
  if (code == current_token_.code())
    current_token_ = lexer_->getNextToken(*current_scope_table_);
  else {
    error(format("'%s' expected", getTokenString(code).c_str()));
    //skipToToken(SEMICOLON);
  }
}

void Parser::match(TokenCode codes[], int n, const std::string& msg) {
  for (int i = 0; i < n; ++i){
    if (codes[i] == current_token_.code()) {
      current_token_ = lexer_->getNextToken(*current_scope_table_);
      return;
    }
  }
  // No tokens were found
  error(msg);
  //skipToToken(SEMICOLON);
}

void Parser::skipToToken(TokenCode code) {
  do {
    nextToken();
  } while (current_token_.code() != code ||
           current_token_.code() != END_OF_FILE);
  nextToken();
}

void Parser::emit(Statement* statement) {
  current_block_->statements_.push_back(statement);
}

VarOperand* Parser::newTemp(DataTypeSpec* type, bool is_array) {
  VariableSymbol* temp_symbol;
  temp_symbol = new VariableSymbol(format("temp_%d", temp_counter_++));
  temp_symbol->set_data_type(*type);
  temp_symbol->set_is_array(is_array);
  temp_symbol->set_kind(LOCAL);
  
  current_scope_table_->insert(temp_symbol);

  VarOperand* temp = new VarOperand;
  temp->symbol_ = temp_symbol;
  
  return temp;
}

TranslationUnit* Parser::parse() {
  std::string identifier;

  while (current_token_.code() != END_OF_FILE) {
    if (current_token_.code() == INT ||
        current_token_.code() == CHAR ||
        current_token_.code() == VOID ) {
      DataTypeSpec* type_spec = parseTypeSpec();
      match(ID);

      if (current_token_.code() == OPEN_PAREN) {
        lexer_->stepBack(3);
        current_token_ = lexer_->getNextToken(*current_scope_table_);
        parseFunction();
      } else {
        lexer_->stepBack(3);
        current_token_ = lexer_->getNextToken(*current_scope_table_);
        parseDeclaration(false);
      }
    } else {
      error("function or variable declaration expected");
      //skipToToken(SEMICOLON);
    }
  }
  return unit_;
}

void Parser::scanDeclarations() {
}

DataTypeSpec* Parser::parseTypeSpec() {
  DataTypeSpec* type_spec = new DataTypeSpec;

  if (current_token_.code() == INT)
    type_spec->data_type_ = INT_TYPE;
  else if (current_token_.code() == CHAR)
    type_spec->data_type_ = CHAR_TYPE;
  else if (current_token_.code() == VOID)
    type_spec->data_type_ = VOID_TYPE;

  TokenCode codes[] = { INT, CHAR, VOID };
  match(codes , 3, "type expected");
  
  //if (current_token_.code() == kAsterisk) {
  //  match(kAsterisk);
  //  type_spec->is_pointer_ = true;
  //}
  
  // This check is not valid since we may parse a function of type 'void',
  // so this will be postponed to the semantic analysis phase.
  //if ((type_spec->data_type_ == kVoidType) && (!type_spec->is_pointer_))
  //  error("invalid type: 'void'");

  // No support for 'const' and pointers yet 
  type_spec->is_const_ = false;
  //type_spec->is_pointer_ = false;
  
  return type_spec;
}

Declaration* Parser::parseDeclaration(bool is_local_decls) {
  std::string identifier;
  bool is_array = false;
  int array_size = 1;

  DataTypeSpec* type_spec = parseTypeSpec();
  Declaration* decl = new Declaration;
  decl->_type = type_spec;

  do {
    if (current_token_.code() == ID) {
      identifier = current_token_.lexeme();
    }
    match(ID);

    // Check if it's an array
    if (current_token_.code() == OPEN_BRACKET) {
      is_array = true;
      match(OPEN_BRACKET);
      if (current_token_.code() == NUM_LITERAL) {
        array_size = current_token_.value();
      }
      match(NUM_LITERAL);
      match(CLOSE_BRACKET);
    }
    
    VariableSymbol* var_symbol = new VariableSymbol(identifier);
    var_symbol->set_data_type(*type_spec);
    var_symbol->set_is_array(is_array);
    var_symbol->set_kind(is_local_decls? LOCAL : ARGUMENT);
    
    // Insert the symbol into the symbol table and Check if function with the
    // same id is already defined.
    if (!current_scope_table_->insert(var_symbol)) {
      error(format("'%s': A symbol with exact name is already defined.",
                   identifier.c_str()));
    }

    // Check for initializations
    if (current_token_.code() == EQUAL) {
      match(EQUAL);
      parseInitials(type_spec, identifier, is_local_decls);
    }
  } while (matchIf(COMMA));
  
  match(SEMICOLON);
  return decl;
}

void Parser::parseInitials(DataTypeSpec* type_spec,
                           const std::string& identifier, 
                           bool is_local_decls) {
  if (current_token_.code() == STRING_LITERAL) {
    // String literal initialization
    std::string string_value = current_token_.lexeme();
    match(STRING_LITERAL);

    // The variable that will be initialized
    VarOperand* left_op = new VarOperand;
    left_op->symbol_ = (*current_scope_table_)[identifier];

    StaticString* static_str = new StaticString(string_value);
    data_section_.push_back(static_str);

    BinaryOp* array_init = new BinaryOp(EQUAL, left_op, static_str);
    ExprStmt* stmt = new ExprStmt(array_init);
    emit(stmt);

  } else if (current_token_.code() == OPEN_BRACE) {
    // Numeric array initialization
    ArrayOperand* array_op = new ArrayOperand;
    array_op->symbol_ = (*current_scope_table_)[identifier];
    
    std::vector<int>* static_arr_values = new std::vector<int>;                                
    int index = 0;

    match(OPEN_BRACE);

    do {
      array_op->index_ = new Literal<int>(INT_TYPE, index++);
      
      if (is_local_decls) {
        BinaryOp* assign = new BinaryOp(EQUAL, array_op, parseExpr());
        ExprStmt* expr_stmt = new ExprStmt(assign);
        emit(expr_stmt);
      } else /* if (!is_local_decls) */ {
        if (current_token_.code() == NUM_LITERAL) {
          static_arr_values->push_back(current_token_.value());
          match(NUM_LITERAL);
        }
      }
    } while (matchIf(COMMA));

    match(CLOSE_BRACE);

    StaticNumericArray* static_num_arr =
      new StaticNumericArray(type_spec->data_type_, static_arr_values);
    data_section_.push_back(static_num_arr);

    VarOperand* left_op = new VarOperand;
    left_op->symbol_ = (*current_scope_table_)[identifier];
    BinaryOp* assign = new BinaryOp(EQUAL, left_op, static_num_arr);
    emit(new ExprStmt(assign));

  } else {
    // Numeric expressoin initialization
    VarOperand* left_op = new VarOperand;
    left_op->symbol_ = (*current_scope_table_)[identifier];
    BinaryOp* assignment = new BinaryOp(EQUAL, left_op, parseExpr());
    ExprStmt* assign_stmt = new ExprStmt(assignment);
    emit(assign_stmt);
  }
}

void Parser::parseFunction() {
  DataTypeSpec* return_type = parseTypeSpec();
  std::string identifier;

  if (current_token_.code() == ID)
    identifier = current_token_.lexeme();

  match(ID);
  match(OPEN_PAREN);

  FunctionSymbol* func_symbol = new FunctionSymbol(identifier, *return_type);
  Function* function = new Function;
  function->symbol_ = func_symbol;

  // Parse function parameters
  if ((current_token_.code() == VOID) ||
      (current_token_.code() == INT)  ||
      (current_token_.code() == CHAR)) {
    do {
      DataTypeSpec* param_type = parseTypeSpec();
      std::string param_id;
      bool is_array = false;

      if (current_token_.code() == ID)
        param_id = current_token_.lexeme();
      match(ID);
      
      if (current_token_.code() == OPEN_BRACKET) {
        match(OPEN_BRACKET);
        is_array = true;

        if (current_token_.code() == NUM_LITERAL) {
          error("Specifing array size is not allowed in function definetion.");
          nextToken();
        }
        match(CLOSE_BRACKET);
      }

      Parameter param(*param_type, param_id, is_array);
      func_symbol->parameters_.push_back(param);

    } while (matchIf(COMMA));
  }
  match(CLOSE_PAREN);

  // Parse function body
  function->body_ = parseBlock();

  // Insert the symbol into the symbol table and Check if function with the
  // same id is already defined.
  if (!current_scope_table_->insert(func_symbol)) {
    error(format("'%s': A symbol with exact name is already defined.",
                 identifier.c_str()));
  }
}

BlockStatement* Parser::parseBlock() {
  blocks_stack_.push(current_block_);
  current_block_ = new BlockStatement;

  match(OPEN_BRACE);
  // for (;;) {
  //   ..
  // }
  match(CLOSE_BRACE);

  BlockStatement* ret = current_block_;

  current_block_ = blocks_stack_.top();
  blocks_stack_.pop();
  
  return ret;
}

Expression* Parser::parseExprStmt() {
  // TODO: parsing assignment expressions
  if (current_token_.code() == ID) {
    std::string ident = current_token_.lexem();
  } else {
    error("identifier expected.");
  }
  return NULL;
}

Expression* Parser::parseExpr() {
  // FIRST(OrExpr)
  if (current_token_.code() == ID || current_token_.code() == NUM_LITERAL ||
      current_token_.code() == OPEN_PAREN || current_token_.code() == MINUS ||
      current_token_.code() == EXCLAMATION) {
    
    Expression* and_expr = parseAndExpr();
    VarOperand* temp_operand;

    while (current_token_.code() == OR) {
      match(OR);
      temp_operand = newTemp(&default_type_, false);
      
      BinaryOp* or_operation = new BinaryOp(OR, and_expr, parseEqualityExpr());
      emit(new ExprStmt(or_operation));

      and_expr = temp_operand;
    }
    return and_expr;
  } else {
    error("Expression expected.");
  }
  return NULL;
}

Expression* Parser::parseAndExpr() {
  
  return NULL;
}

Expression* Parser::parseEqualityExpr() {

  return NULL;
}
